![]() |
![]() |
|
Das ist schon alles! Unser Attribut soll nur wie eine boolesche Variable operieren, die gesetzt ist oder nicht. Daher benötigen wir in diesem Fall auch keinen weiteren Programmcode. Zwei Fakten sind ausschlaggebend, um aus einer Klassendefinition ein Attribut zu machen: 1. Der Definition eines benutzerdefinierten Attributs selbst geht immer die Definition des Attributs AttributeUsageAttribute voraus.
Die Voranstellung eines Attributs mit einer Parameterliste vor der eigentlichen Attributdefinition hat den Grund, hier bereits elementare Eigenschaften der neuen Attributklasse festzulegen. In diesem Zusammenhang sind drei Parameter besonders interessant:
Während AttributeTargets angegeben werden muss, sind die beiden anderen optional. AttributeTargetsJedes Attribut kann sich nur auf bestimmte Codeelemente auswirken. Diese werden mit AttributeTargets bekannt geben. Im Beispiel oben wird mit AttributeTargets.All zum Ausdruck gebracht, dass wir das benutzerdefinierte Attribut auf alle Elemente anwenden können, aber das muss nicht immer so sein. Man kann den Einsatz eines Attributs ebenso gut nur auf Methoden oder Felder beschränken. Es steht dabei immer die Frage im Vordergrund: Was soll das Attribut letztendlich bewirken, welche Elemente sollen über das Attribut beeinflusst werden? AttributeTargets ist in der .NET-Klassenbibliothek als Enumeration vordefiniert und enthält die in der folgenden Tabelle aufgeführten Entitäten.
Natürlich steht auch hinter AttributeUsageAttribute eine Klassendefinition, die im Namespace System zu finden ist. Im Grunde genommen unterscheidet sich eine Klasse, die ein Attribut beschreibt, nicht von einer herkömmlichen Klasse. Daher verwundert es nicht, dass die Klasse AttributeUsageAttribute einen Konstruktor definiert, der einen Parameter vom Typ AttributeTargets erwartet:
Der Parameter validon vom Typ AttributeTargets wird bitweise interpretiert. Jedes Bit beschreibt dabei ein Element, auf welches das Attribut angewendet werden kann. Möchte man das Attribut mehreren verschiedenen Elementen zugänglich machen, muss man mehrere AttributeTargets-Konstanten bitweise verknüpfen, z.B.:
In diesem Fall wird ein Attribut bereitgestellt, das sowohl Methoden als auch Eigenschaften als Informationsquelle dienen kann. InheritedEine Klasse kann ihre Mitglieder einer abgeleiteten Klasse vererben. Einem Entwickler stellt sich natürlich die Frage, ob das Attribut in den Vererbungsprozess mit einbezogen wird oder ob es Gründe gibt, es davon auszuschließen. Einem benutzerdefiniertem Attribut teilen wir dies durch den booleschen Parameter Inherited mit, den wir optional AttributeUsageAttribute übergeben können. Standardmäßig ist der Wert auf true festgelegt, demnach vererbt sich ein gesetztes Attribut in einer Vererbungshierarchie weiter. AllowMultipleIn wohl eher seltenen Fällen kann es erforderlich sein, ein Attribut demselben Element mehrfach zuzuweisen. Diese Situation wäre denkbar, wenn man über das Attribut einem Element mehrere Feldinformationen zukommen lassen möchte. Dann muss man die mehrfache Anwendung eines Attributs explizit gestatten. Zur Lösung geben Sie den Parameter
an. Verzichten Sie auf diese Angabe, kann ein Attribut per Definition mit einem bestimmten Element nur einmal verknüpft werden. Konstruktoren eines AttributsWeiter oben haben Sie bereits erfahren, dass es möglich ist, in einer Attributklasse einen Konstruktor zu definieren. Im folgenden Beispiel ist das Attribut DeveloperAttribute definiert, das zwei Datenmember enthält, von denen einer durch den Konstruktoraufruf initialisiert wird. Durch AttributeTargets geben wir bekannt, dass das Attribut jedem Codeelement angeheftet werden kann.
Der Konstruktor nimmt einen Parameter entgegen, nämlich den Wert für das Feld Zuname. Bevor Sie sich darüber Gedanken machen, wie man das Feld PersID initialisiert, sehen Sie sich an, wie das Attribut auf eine Klasse angewendet wird:
Mit dieser Definition wird der Konstruktor unter Übergabe einer Zeichenfolge aufgerufen. Das zweite Feld des Attributs (PersID) wird mit keinem bestimmten Wert initialisiert, es enthält 0. Selbstverständlich könnten wir innerhalb der Attributdefinition für PersID einen von 0 abweichenden Wert festlegen, aber dieser ist dann natürlich für jedes verknüpfte Element identisch. Positionale und benannte ParameterUm PersID einen individuellen Wert zuzuweisen, lässt sich DeveloperAttribute auch wie folgt an die Klasse heften:
Beachten Sie, dass wir jetzt zwei Argumente übergeben, obwohl der Konstruktor nur einen Parameter vorsieht. Dies ist ein besonderes Merkmal von Attributen, denn beim Initialisieren eines Attributs können Sie sowohl positionale als auch benannte Parameter verwenden.
In unserem Beispiel ist Zuname ein positionaler Parameter, dem die Zeichenfolge »Meier« übergeben wird, während PersID ein benannter Parameter ist. Benannte Parameter sind sehr flexibel. Einerseits können sie Standardwerte aufweisen, die grundsätzlich immer gültig sind, andererseits kann der Wert im Bedarfsfall individuell festgelegt werden. Ob Sie einen Datenmember positional oder benannt einsetzen wollen, ist die erste Entscheidung, die sich an den spezifischen Anforderungen orientiert. Ob ein benannter Parameter neu festgelegt werden muss oder der Standardwert akzeptabel ist, hängt vom Einzelfall ab. Die Möglichkeit, benannte Parameter vorzusehen, befreit Sie von der Verpflichtung, für jede denkbare Kombination von Feldern und Eigenschaften überladene Konstruktoren in der Attributdefinition vorsehen zu müssen. Andererseits wird Ihnen damit aber nicht die Alternative entzogen, dennoch den Konstruktor zu überladen. Da unterscheiden sich die herkömmlichen Klassendefinitionen nicht von denen der Attribute.
Die Reihenfolge der benannten Parameter ist beliebig, da der Compiler aufgrund der Parameternamen richtig zuordnen kann. Benannte Parameter können alle öffentlich deklarierten Felder oder Eigenschaften sein – vorausgesetzt, sie sind weder statisch noch konstant definiert. Die Daten eines Attributs auswertenEin Attribut soll einen steuernden Einfluss auf das Laufzeitverhalten eines Elements ausüben oder es mit wesentlichen Informationen versorgen. Enthält das Attribut Daten, ist es für das mit einem Attribut markierte Element wichtig, an die Daten des Attributs zu gelangen. Im folgenden Codefragment soll dies für das Beispiel unseres eben entwickelten DeveloperAttributes gezeigt werden. Dazu schreiben wir den folgenden Code:
An dieser Stelle sei darauf hingewiesen, dass wir in der Implementierung von Main davon ausgehen, dass nur unser benutzerdefiniertes Attribut DeveloperAttribute gesetzt ist – ein Element kann natürlich auch mit mehreren Attributen verknüpft sein. Im Code müssten wir dann die Klasse auf alle gesetzten Attribute abfragen. Um die Daten eines Attributs auszuwerten, benötigen wir zunächst eine Referenz vom Typ des gesuchten Attributs, der wir das der Klasse angeheftete Attribut zuweisen. Mit den uns bisher bekannten Hilfsmitteln ist das allerdings nicht möglich, wir benötigen dazu eine Methode der Klasse Attribute, die uns die gewünschte Referenz liefert: Es ist die vielfach überladene Methode GetCustomAttribute. In unserem Fall eignet sich die folgende Variante:
Im ersten Argument geben wir den Typ des attributführenden Objekts, im zweiten Parameter den des Attributs an. Mit
erhalten wir nach entsprechender Typkonvertierung die Referenz auf das Attribut vom Typ DeveloperAttribute, das die Klasse Class1 ziert. Da wir nun die Referenz auf das Attribut in den Händen halten, könnten wir auf dessen öffentliche Member zugreifen. Um ganz sicherzugehen, dass das Attribut auch tatsächlich mit der Klasse verschmolzen ist, starten wir noch eine Abfrage (auf die wir allerdings in diesem Beispiel auch verzichten könnten), ob die Variable attr auch wirklich eine gültige Referenz enthält. Würde das Attribut nämlich nicht gefunden, wäre attr gleich null.
An der Konsole wird damit
ausgegeben. Wenn Sie das Beispiel in der Entwicklungsumgebung nachvollziehen, sollten Sie die Attributverknüpfung entfernen und die Anwendung nochmals starten. Das Attribut wird nicht gefunden und an der Konsole
angezeigt.
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.